from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes  # 导入加密相关模块
from cryptography.hazmat.backends import default_backend  # 导入默认后端模块
from base64 import b64encode, b64decode  # 导入Base64编码解码模块
import json  # 导入JSON处理模块
from datetime import datetime  # 导入日期时间处理模块


# 定义一些辅助类
class RefundInfo:
    def __init__(self):
        self.refundDate = None  # 退款日期
        self.refundReason = None  # 退款原因
        self.originalOrderNo = None  # 原订单号
        self.refundPlatformOrderNo = None  # 平台退款订单号


class PaymentContentBo:
    def __init__(self):
        self.orderNo = None  # 订单号
        self.platformOrderNo = None  # 平台订单号
        self.orderStatus = None  # 订单状态
        self.orderFinishTime = None  # 订单完成时间
        self.succAmount = None  # 成功金额
        self.failAmount = None  # 失败金额
        self.refundInfo = None  # 退款信息


class PaymentCallBackBo:
    def __init__(self, type, content):
        self.type = type  # 回调类型
        self.content = content  # 回调内容


class PayOrderStatusEnum:
    SUCCESS = 'SUCCESS'  # 订单成功状态


class PayCallBackTypeEnum:
    APP_PAY_REFUND = 'APP_PAY_REFUND'  # 应用支付退款类型


def jsonObjectSerializer(obj):
    return json.dumps(obj, default=lambda o: o.__dict__)  # 将对象序列化为JSON字符串


class EncryptUtils2:
    ALGORITHM = "AES"  # 加密算法
    MODE = "AES/ECB/PKCS5Padding"  # 加密模式

    @staticmethod
    def encrypt(data, key):
        """
        使用AES加密数据
        :param data: 待加密的原始数据
        :param key: 加密密钥
        :return: 加密后的数据字符串
        """
        try:
            data_bytes = data.encode('utf-8')  # 将数据转换为字节
            key_bytes = key.encode('utf-8')  # 将密钥转换为字节

            # 创建 AES 密钥
            cipher = Cipher(algorithms.AES(key_bytes), modes.ECB(), backend=default_backend())
            encryptor = cipher.encryptor()  # 创建加密器

            # 填充数据
            padded_data = EncryptUtils2.pad(data_bytes)

            # 加密数据
            result = encryptor.update(padded_data) + encryptor.finalize()  # 执行加密操作

            return b64encode(result).decode('utf-8')  # 返回Base64编码后的加密结果

        except Exception as e:
            raise Exception("encrypt fail!", e)  # 抛出加密失败异常

    @staticmethod
    def decrypt(data, key):
        """
        使用AES解密数据
        :param data: 待解密的加密数据字符串
        :param key: 解密密钥
        :return: 解密后的原始数据
        """
        try:
            data_bytes = b64decode(data.encode('utf-8'))  # 将Base64编码的数据转换为字节
            key_bytes = key.encode('utf-8')  # 将密钥转换为字节

            # 创建 AES 密钥
            cipher = Cipher(algorithms.AES(key_bytes), modes.ECB(), backend=default_backend())
            decryptor = cipher.decryptor()  # 创建解密器

            # 解密数据
            result = decryptor.update(data_bytes) + decryptor.finalize()  # 执行解密操作

            # 去除填充
            unpadded_result = EncryptUtils2.unpad(result)

            return unpadded_result.decode('utf-8')  # 返回解密后的原始数据

        except Exception as e:
            raise Exception("decrypt fail!", e)  # 抛出解密失败异常

    @staticmethod
    def pad(data):
        """
        对数据进行PKCS5填充
        :param data: 待填充的数据字节
        :return: 填充后的数据字节
        """
        block_size = 16  # 块大小
        padding = block_size - (len(data) % block_size)  # 计算需要填充的字节数
        return data + bytes([padding]) * padding  # 返回填充后的数据

    @staticmethod
    def unpad(data):
        """
        对数据进行PKCS5去填充
        :param data: 待去填充的数据字节
        :return: 去填充后的数据字节
        """
        padding = data[-1]  # 获取最后一个字节
        if padding > 0 and padding <= 16:  # 检查填充是否有效
            return data[:-padding]  # 返回去除填充后的数据
        else:
            raise ValueError("Invalid padding")  # 抛出无效填充异常


def main():
    """
    主函数，用于模拟支付回调数据的加密
    test5环境
             * 			账户中心：
             * 				payment.trade.salt：ClwAv2ZDHLzeleTk
             * 				payment.trade.aesKey: i+xdrftm6yXNVXXg
             * 			月卡中心：
             * 				payment.trade.salt：S87sNp4toQsP5/Pf
             * 				payment.trade.aesKey: b4AHimzQY9ZQqugc
             *
             *
             * 			test6环境
             * 			账户中心：
             * 				payment.trade.salt：6p65xZqDsjbdPL3B
             * 				payment.trade.aesKey: uSEViBNu16DlQlO4
             * 			月卡中心：
             * 				payment.trade.salt：9DX1Eok94cx14efi
             * 				payment.trade.aesKey: xtF8f61kM7fsxdTI
             *
             *
             * 			uat环境
             * 			账户中心：
             * 				payment.trade.salt：p58x206YYoeargKI
             * 				payment.trade.aesKey: /f0BjhR3qVDiZDQI
             * 			月卡中心：
             * 				payment.trade.salt：XMFG4o8YrXUl7JlD
             * 				payment.trade.aesKey: rF68yo13jAVObW6m
             *
             * 			run环境
             * 			账户中心 cab-account-center.yml
             * 				payment.trade.salt: 'vWTNBEdj1UrN7QLG'
             * 				payment.trade.aesKey: 'Rlly/E6Jyvc7KXDO'
             *
             *
             * 			月卡中心 cab-periodic-activity-center.yml
             * 				payment.trade.salt: 'zFwnfLg6fIwi+bu+'
             * 				payment.trade.aesKey: 's3kxAgwCT6b2c2TY'
    """
    try:
        # 关键参数设置
        order_orderNo = "945320917813927937"  # 设置订单号
        refundPlatformOrderNo = "PO2040303202411211407502990001"  # 设置平台退款订单号
        salt = "S87sNp4toQsP5/Pf"  # 设置盐值
        aes_key = "b4AHimzQY9ZQqugc"  # 设置AES密钥

        succecc_amount = 0  # 设置成功金额
        refundDate = int(datetime.now().timestamp() * 1000)  # 获取当前时间戳并转换为毫秒

        refundInfo = RefundInfo()  # 创建退款信息实例
        refundInfo.refundDate = refundDate  # 设置退款日期
        refundInfo.refundReason = "mock退款回调，手工回调"  # 设置退款原因
        refundInfo.originalOrderNo = order_orderNo  # 设置原订单号
        refundInfo.refundPlatformOrderNo = refundPlatformOrderNo  # 设置平台退款订单号

        paymentContentBo = PaymentContentBo()  # 创建支付内容实例
        paymentContentBo.orderNo = f"refund{order_orderNo}"  # 设置订单号
        paymentContentBo.platformOrderNo = None  # 设置平台订单号
        paymentContentBo.orderStatus = PayOrderStatusEnum.SUCCESS  # 设置订单状态
        paymentContentBo.orderFinishTime = refundDate  # 设置订单完成时间
        paymentContentBo.succAmount = succecc_amount  # 设置成功金额
        paymentContentBo.failAmount = 0  # 设置失败金额
        paymentContentBo.refundInfo = refundInfo  # 设置退款信息

        payment_call_back_bo = PaymentCallBackBo(
            type=6,  # 设置回调类型
            content=paymentContentBo  # 设置回调内容
        )

        encrypted_data = EncryptUtils2.encrypt(jsonObjectSerializer(payment_call_back_bo) + salt, aes_key)  # 加密数据
        print(encrypted_data)  # 输出加密后的数据

    except Exception as e:
        print(e)  # 输出异常信息


if __name__ == '__main__':
    main()  # 调用主函数
